﻿#include "rec_write_avi.h"
#include "serial_file_com.h"
#include "serial_file_com_nonebuf.h"


extern StorageConfig	g_storageCfg;
#define mmioFOURCC0(ch) ( (unsigned int)((unsigned char)(ch)) )

#define mmioFOURCC1(ch0, ch1, ch2, ch3) \
	( (mmioFOURCC0(ch0))       | (mmioFOURCC0(ch1) << 8) | \
	(mmioFOURCC0(ch2) << 16) | (mmioFOURCC0(ch3) << 24) )


#define VIDEO_PAL_FORMAT       0
static char id_str[MAX_INFO_STRLEN] = {0};
const int g_max_bit_rate = 512;

#define OUT4CC(s) \
	if(nhb<=HEADERBYTES-4) memcpy(m_AVI_header+nhb,s,4); nhb += 4

#define OUTLONG(n) \
	if(nhb<=HEADERBYTES-4) long2str(m_AVI_header+nhb,n); nhb += 4

#define OUTSHRT(n) \
	if(nhb<=HEADERBYTES-2) { \
	m_AVI_header[nhb  ] = (n   )&0xff; \
	m_AVI_header[nhb+1] = (n>>8)&0xff; \
	} \
	nhb += 2

static void long2str(unsigned char *dst, int n)
{
	dst[0] = (n    )&0xff;
	dst[1] = (n>> 8)&0xff;
	dst[2] = (n>>16)&0xff;
	dst[3] = (n>>24)&0xff;
}

CAviFileWrite::CAviFileWrite(int channel)
{
	m_channel = channel;

	fdes = 0;              /* File descriptor of AVI file */
	mode = 0;              /* 0 for reading, 1 for writing */

	m_width  = 0;             /* Width  of a video frame */
	m_height = 0;            /* Height of a video frame */
	m_fps    = 30;               /* Frames per second */
	memset(m_compressor,0,8);     /* Type of compressor, 4 bytes + padding for 0 byte */
	memset(compressor2,0,8);     /* Type of compressor, 4 bytes + padding for 0 byte */
	video_strn = 0;        /* Video stream number */
	m_video_frames = 0;      /* Number of video frames */
	memset(video_tag,0,4);
				//(if index present) */
	max_len = 0;    /* maximum video chunk present */

	//track_t track[AVI_MAX_TRACKS];  // up to AVI_MAX_TRACKS audio tracks supported
	memset(track,0,sizeof(track_t)*AVI_MAX_TRACKS);
	m_idx_no = 0;             /* number of index entries actually filled */


	v_codech_off = 0;       /* absolut offset of video codec (strh) info */
	v_codecf_off = 0;       /* absolut offset of video codec (strf) info */

	//unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */
	//video_index_entry *video_index;
    video_index = NULL;
	last_pos = 0;          /* Position of last frame written */
	last_len = 0;          /* Length of last frame written */
	m_must_use_index = 0;              /* Flag if frames are duplicated */
	m_movi_start = 0;

	m_anum = 0;            // total number of audio tracks
	m_aptr = 0;            // current audio working track

	InitFifoBuf();

	m_max_idx = 4096;           /* number of index entries actually allocated */

	m_AVI_header = m_headBuf;
	m_aviIdx_bufsz = m_max_idx * 16 + 8 ;
	m_aviIdx = new uchar_t[m_aviIdx_bufsz];

	if(m_aviIdx == 0)
	{
		TRACE_ERR("new AVI index buf fail.\n");
	}

	memset(m_aviIdx,0,m_aviIdx_bufsz);

	if( NULL == m_pFile)
	{
		m_pFile = new CSerialFileComNobuf();
	}
	m_fileHeadOffset= SIZE_KB(2); //将线性文件头信息放置1KB
	m_pRecSwitch    = CRecSwitchRule::GetInstance();
	m_endTime = 0;
	bitmap_info_header = NULL;
}

CAviFileWrite::~CAviFileWrite()
{
	if( NULL!= m_aviIdx)
	{
		delete [] m_aviIdx;
	}
}

int CAviFileWrite::Open(const char * file_name, uint32_t file_index, uint32_t file_size, int64_t startTime,PSTFS_RECINFO_EXTERN pExternInfo)
{
	int ret = 0;
	if( NULL == file_name ||  0 == strlen(file_name))
	{
		return ERR_NO_DEVICE;
	}

	uint64_t fileLBA = 0;

	fileLBA = file_index * 256;;
	if(SWITCH_BY_TIME == m_pRecSwitch->GetSwitchMod())
	{
		file_size = 0xFFFFFFFF;// 当文件是按时间分段时，把文件长度设置为最大。
	}

	ret = m_pFile->Open((void*)file_name,fileLBA,file_size,SCSI_RDWR);
	if(ret < 0)
	{
		TRACE_ERR("Open %s fail. ret %d .\n",file_name,ret);
		return ret;
	}

	//初始化头信息
	InitHeadInfo(file_size,file_index,startTime,pExternInfo);

	//清理关键帧索引信息
	ret = ClearIndexArea();
	if(ret < 0)
	{
		TRACE_ERR("ch%02d clear index fail. ret %d \n ",m_channel,ret);
		return ret;
	}
	m_frmIdx.Reset();

	AVI_set_video(pExternInfo->vWidth,pExternInfo->vHeight,pExternInfo->vRate,(char*)"H264");
	AVI_set_audio(pExternInfo->audio_channels,pExternInfo->audio_rate,pExternInfo->audio_bit,pExternInfo->audio_fmt,0);

	ret = avi_update_header();

	//开启缓存刷新线程
	m_curPos = m_fileHead.dataOffset;
	m_switchMod = m_pRecSwitch->GetSwitchMod();

	ret = WriteRecHeadInfo();

	Start_FlushData(m_curPos);

	m_movi_start   = m_curPos;
	m_idx_no       = 0;
	m_video_frames = 0;      /* Number of video frames */
	m_bstop        = false;
	m_bsuspense    = false;
	m_anum         = 0;
	m_aptr         = 0;

    TRACE("ch%02d open file OK %s fileSize %u MB startoffset %d .\n",m_channel,file_name,BYTE_TO_MB(file_size),m_curPos);

	return 0;
}

int  CAviFileWrite::OpenRepairFile(REC_FILE_INFO & recInfo,uint32_t fileLen,int64_t startTime)
{
	int ret  = 0;
    STFS_REC_IDX * pRecIdx  = NULL;

	uint64_t fileLBA = 0;
	char * dev = "/dev/mmcblocksp01";
	if(SWITCH_BY_TIME == m_pRecSwitch->GetSwitchMod())
	{
		fileLen = 0xFFFFFFFF;// 当文件是按时间分段时，把文件长度设置为最大。
	}
	ret = m_pFile->Open(dev,fileLBA,fileLen,SCSI_RDWR);
	if(ret)
	{
		TRACE("Open fileIndex fail %d ret %d \n",recInfo.indexNo,ret);
		return ret;
	}

	InitFifoBuf();

	m_frmIdx.Open(STFS_REC_DATA_OFFSET,m_fileHead.idxOffset,m_pFile->get_sec_size());
	uchar_t * pBuf = m_frmIdx.GetBufAddr();

	//读取
	ret = m_pFile->ReadPos(m_fileHeadOffset,pBuf,STFS_REC_DATA_OFFSET);
	if(ret < 0)
	{
		goto ERR_OUT;
	}

	//1. 处理文件头部
	m_fileHead.fileSpace = fileLen;
	memcpy(&m_fileHead,pBuf,REC_HEADINFO_LEN);

	//2.录像段
	memset(&m_curSeg,0,REC_SEG_LEN);
	m_curSeg.segNO      = recInfo.segIndex + 1;
	//m_curSeg.data_start = m_curSeg.data_endpos = recInfo.endOffset;
	m_curSeg.tmStart	= recInfo.tmStart;

	memset(&m_recIdx,0,REC_INDEX_LEN);
	//3.索引区
	pRecIdx = (STFS_REC_IDX *)(pBuf + (STFS_REC_IDX_OFFSET -m_fileHeadOffset));

	m_frmIdx.AnalyseData();

	//刷新头文件信息进去
  	m_curSeg.data_endpos = m_curSeg.data_start = m_curPos = recInfo.endOffset;

	Start_FlushData(m_curPos);

	m_endTime          = startTime;
	TRACE("CH%02d dev %s Open Repair file index %d OK last offset %d\n",
		m_fileHead.channel,dev,m_fileHead.indexNO,m_fileHead.fileLen);

	return 0;
ERR_OUT:
	m_pFile->Close();
	return ret;
}

int CAviFileWrite::avi_wirte_index()
{
	int ret = 0;
	uint32_t data_size = m_max_idx * 16 + 8;

	ret = m_pFile->WritePos(m_curPos,m_aviIdx,data_size);

	return ret;
}



int CAviFileWrite::Close()
{
	if(m_pFile->is_closed())
	{
		return ERR_FILE_NOOPEN;
	}

	int ret = 0;
	ret = avi_wirte_index();
	//先写AVI信息头
    AVI_close();
	TRACE("Time seg %lld secs offset %d  .\n ",(m_fileHead.endTime - m_fileHead.startTime)/1000,m_curPos);
	//然后再刷新缓存
	CRecWriteBase::Close();


	m_idx_no = 0;             /* number of index entries actually filled */

	return 0;
}

bool CAviFileWrite::IsContextCreateIdx(int frmType, int64_t time)
{
	//非I帧,不建立索引
	if(frmType != IFrame)
	{
		return false;
	}

	//使用偏移来建立索引
	{   //解决索引空间用完，但是文件还剩余还很多的情况
		uint32_t frmDiff = m_curPos - m_recIdx.offset;
		if(frmDiff <  m_idxDiffComSize)
		{
			return false;
		}
	}
	return  true;
}

int CAviFileWrite::WriteFrame(uchar_t * data_buf, uint32_t data_size)
{
	int		ret         = 0;	//返回值
	char  * pFrame      = NULL;
	uint32_t avi_idx_sz = m_idx_no *16  + 8;
	uint32_t destOffset = 0 ;
	m_frmTool.Init(data_buf);

	int64_t time = m_frmTool.GetFrameTime();
	int frmType  = m_frmTool.GetFrameType();
	int  frmLen   = m_frmTool.GetFrameLen(false);
	//检查文件是否超界
	if(SWITHC_BY_SZMB == m_pRecSwitch->GetSwitchMod())
	{
		destOffset = m_curPos + frmLen + AVI_MOVBOX_HD_SZ  + avi_idx_sz;
		if(m_fileHead.fileSpace != 0 && destOffset > m_fileHead.fileSpace)
		{
			TRACE_ERR("CH%02d Data is over file.dataSize %d curPos %d destOffset %d file size %d .\n",
				m_fileHead.channel,data_size,m_curPos,destOffset,m_fileHead.fileSpace);
			return ERR_DATA_OVER_FILE;
		}

		if( frmType == IFrame &&
			GetFreeSize() < SIZE_1MB)
		{
			ret = AnsureKeyFrameSpace();
			if( ret < 0)
			{   // 剩余的空间大小不够存储两个I之间完整的视频数据

				return ERR_FOR_SAVE_KEY_FRAME;
			}
		}

	}
	else
	{   //判定为最后一个P帧时再去切换文件，避免下一下文件为
		int diff = (time - m_fileHead.startTime)/1000 + 1;
		int sw_param = m_pRecSwitch->GetSwitchParm();
		if( frmType == IFrame &&
			diff > sw_param)
		{
		    if(0 == (time / 1000) % 60)  //
		    {
                return ERR_OVER_MAX_TIMESEC;
		    }
		}
	}

	bool bKeyFrm = true;

	frmLen = PAD_EVEN(frmLen);
	int dest_size = frmLen + AVI_MOVBOX_HD_SZ;

	if( IFrame == frmType)
	{
		bKeyFrm = true;
	}
	else
	{
		bKeyFrm = false;
	}
	//检查索引区是否已经满了
	if( m_recIdx.NO    >= STFS_MAX_RECIDX_NUM ||
		m_curSeg.segNO >= STFS_MAX_SEG_NUM)
	{
		TRACE_ERR("Over max index NUM or segment.curPos %d index %d.seg %d,\n",
				  m_curPos,m_recIdx.NO,m_curSeg.segNO);
		return ERR_SPACE_NOTENOUGH;
	}

	if( 0 == m_recIdx.NO )
	{
		if(!bKeyFrm)
		{
			return ERR_NOT_KEY_FRAME_1STVEDIO;
		}
	}

	if( bKeyFrm &&
		GetFreeSize() < SIZE_1MB)
	{
#if 0
		ret = AnsureKeyFrameSpace();
		if( ret < 0)
		{
			return ERR_FOR_SAVE_KEY_FRAME;
		}
#endif
	}

	//建立视频关键帧索引
	pFrame = (char *)(data_buf + BASE_FRAME_HEAD_LEN - 8);

	if ( AFrame == frmType)
	{   //标准ASF文件建立的索引
		ret++;
	//	avi_write_data(,,1,0);
	//	AVI_write_audio(pFrame,frmLen);
	//	track[m_aptr].audio_bytes += dest_size;
		return 0;
	}
	else
	{
	//	AVI_write_frame(pFrame,frmLen + 8,(int)bKeyFrm);
		ret = avi_write_data(pFrame,frmLen,0,bKeyFrm);
		last_len = dest_size;
		m_video_frames++;
	}

	if(ret < 0)
	{
		return ret;
	}

	if(bKeyFrm && IsContextCreateIdx(frmType,time))
	{
		CreateRecIndex(frmType,dest_size,time,m_curPos);
	}

	m_fileHead.endTime  = m_endTime = time;
	m_curPos += dest_size;

	return dest_size;
}


//重建索引

int CAviFileWrite::Repair_head_info()
{
	int ret =0;

	//自建立文件头
	m_fileHead.fileLen     = m_curPos;
	m_fileHead.endTime     = m_endTime;

	m_fileHead.writeStatus = STFS_REC_END_FLAG;
	memcpy(m_headBuf + m_fileHeadOffset,&m_fileHead,REC_HEADINFO_LEN);

	ret = m_pFile->WritePos(0,m_headBuf,m_headBufSize);
	if(ret < 0)
	{
		TRACE_ERR("CH%02d Write head info fail.\n",m_fileHead.channel);
		return ret;
	}

	return ret;
}

int CAviFileWrite::RecFullStauts(uint32_t offset ,int64_t time)
{
	int leaf = 0;

	if(SWITCH_BY_TIME == m_pRecSwitch->GetSwitchMod())
	{
		return ERR_DATA_OVER_FILE;
	}
	else
	{
		return ERR_DATA_OVER_FILE;
	}

	return leaf;
}

uint32_t CAviFileWrite::GetWritePercent()
{
	if(SWITCH_BY_TIME == m_pRecSwitch->GetSwitchMod())
	{
		int diff = (m_endTime - m_fileHead.startTime)/1000;
		return (diff * 100 / m_pRecSwitch->GetSwitchParm());
	}
	else
	{
		return (uint32_t)((float)m_curPos*100/(float)m_fileHead.fileSpace);
	}
}

int  CAviFileWrite::AnalysisFileInfo(uchar_t * pBuf)
{
	int ret = 0;
	//检查是否为AVI文件头
	RIFF_HEAD * pFiffHead =(RIFF_HEAD*)m_headBuf;
	if(pFiffHead->fccType != mmioFOURCC('R','I','F','F') )
	{
		return ERR_NOT_AVI_FILE;
	}

//	pFiffHead->size =;

	return ret;
}

int  CAviFileWrite::AnalysisAVIData(uint32_t dataPos, uchar_t * pBuf,int dataSize,uint32_t & effSize,vector<STFS_REC_IDX> & vecIdx,uint32_t & detect_cnt)
{
	int ret = 0;
	//修复录像数据的时候，视频索引可以重建
	//00db "00db" 不对音频做处理
	RIFF_HEAD * pRiff    = (RIFF_HEAD*)pBuf;
	uint32_t    fccT_video  =  mmioFOURCC('0','0','d','b');
	uint32_t    fccT_audio1 = mmioFOURCC('0','1','w','b');
	uint32_t    fccT_audio2 = mmioFOURCC('0','2','w','b'); //"0%1dwb"
	int         offset   = 0;
	int         naluType = 0;
	uint32_t    pkt_size = 0;
	uchar_t *   pNalu    = NULL;
	effSize = 0;

	while (true)
	{
		if(offset >= dataSize)
		{
			break;
		}

		pRiff = (RIFF_HEAD*)(pBuf + offset);
		if(pRiff->fccType == fccT_video )
		{
			pNalu    = (uchar_t *)(pRiff + 4);
			naluType = pNalu[4]&0x1f;

			//判定是否为帧
			if( H264NT_SLICE_IDR == naluType)
			{
				m_recIdx.frameType = IFrame;
				if(pRiff->size & 0x01)
				{
					m_recIdx.frameLen  = pRiff->size +1;
				}
				else
				{
					m_recIdx.frameLen  = pRiff->size ;
				}

				m_recIdx.tmMS      = (uint32_t)(m_endTime);
				m_recIdx.offset     = dataPos;
				m_recIdx.magic      = STFS_REC_IDX_SIG;
				m_recIdx.NO++;
				if(0 != detect_cnt)
				{   //过滤第一个关键帧，
					vecIdx.push_back(m_recIdx);
				}
				detect_cnt++;
			}
			pkt_size  =   8 + pRiff->size;
			offset    +=  pkt_size;
			dataPos   +=  pkt_size;  //实际数据偏移量
			m_endTime +=  1000/m_fps ;      //帧时间
			effSize   +=  pkt_size;
			m_video_frames++;
			continue;
		}
		else
		{
			if(pRiff->fccType  == fccT_audio1)
			{   //检测到音频数据继续向前推进
				pkt_size   = 8 + pRiff->size;
				effSize   +=  pkt_size;
				offset    +=  pkt_size;
				continue;
			}
			else
			{
				return ERR_NOT_AVI_FRAME;
			}
		}
	}

	return ret;
}

//修复ASF录像
int CAviFileWrite::RepairFile(char*  diskName,uint32_t fileIndex,uint64_t fileLBA,uint32_t recSize,int64_t startTime,uint32_t& fileSize,int64_t & endTime)
{
	int ret = 0;
	vector<STFS_REC_IDX> vecIdx;
	bool bAVI_File = false;

	if( NULL == diskName ||  0 == strlen(diskName))
	{
		return ERR_NO_DEVICE;
	}

	CSerialFileComNobuf recFile;
	char * fileName = diskName;
	ret = recFile.Open(fileName,fileLBA,recSize,SCSI_RDWR);
	if(ret < 0)
	{
		TRACE_ERR("RepairFile Open fileLBA %lld size %d \n",fileLBA,recSize);
		return ret;
	}

	uchar_t * pBuf = m_headBuf;
	ret = recFile.ReadPos(0,pBuf,m_headBufSize);
	if(ret < 0)
	{
		TRACE_ERR("RepairFile read head data size %d fail.\n",m_headBufSize);
		recFile.Close();
		return ret;
	}

	ret = AnalysisFileInfo(m_headBuf);
	if(ret < 0)
	{
		return ret ;
	}

	ret = DetectFileHead(pBuf,STFS_REC_IDX_OFFSET);
	if(ret < 0)
	{
		TRACE_ERR("Not a record file.\n");
		recFile.Close();
		return ret;
	}

	m_fileHead.fileSpace = get_file_sie(diskName);

	uint32_t  bufSize    = SIZE_2MB;
	uchar_t * frameBuf   = new uchar_t[bufSize];
	ret = m_frmIdx.Open(STFS_REC_DATA_OFFSET,m_fileHead.idxOffset,m_pFile->get_sec_size());
	if(ret < 0)
	{
		TRACE_ERR("CH %d idx %d File is writting.\n",m_fileHead.channel,m_fileHead.indexNO);
	}

	//读取数据
	pBuf = m_frmIdx.GetBufAddr();
	uint32_t ioSize = m_frmIdx.GetBufSize();
	ret = recFile.ReadPos(m_fileHead.idxOffset,pBuf,ioSize);
	if(ret < 0)
	{
		TRACE_ERR("RepairFile read head data size %d fail.\n",m_headBufSize);
		if( NULL != frameBuf)
		{
			delete [] frameBuf;
		}
		return ret;
	}

	//解析一下索引区的信息，找出最后有效I帧的结束偏移
	m_frmIdx.AnalyseData();
	//重新解析一下之前索引
//	Rebuild_simple_index();
	m_recIdx.NO = m_frmIdx.GetFrmIdxNum() - 1;
	uint32_t start_chk_offset = m_frmIdx.GetLastFrmOffset();
	uint32_t lastFrmLen = m_frmIdx.GetLastFrmLen();
	fileSize = start_chk_offset + lastFrmLen;
	endTime  = m_endTime = (int64_t)m_frmIdx.GetLastFrmTime() * 1000;
	if(startTime> endTime)
	{
		endTime = m_endTime = startTime;
	}
	//重建索引

	uint32_t ioPos     = start_chk_offset;
	uint32_t endOffset   = 0;
	uint32_t curFrmLen   = 0;
	uint32_t eff_frmSize = 0;  //每次读取数据，有效的数据
	uint32_t detect_cnt  = 0;
	bool b1stIFrm       = true;
	int  data_pkt_len  = SIZE_2MB;

	ioSize = bufSize;
	endOffset = start_chk_offset + lastFrmLen;
	m_video_frames = m_recIdx.NO * m_fps;

	while ((ioPos + 16) < m_fileHead.fileSpace)
	{
		if((ioPos + bufSize) > m_fileHead.fileSpace)
		{
			ioSize  = m_fileHead.fileSpace - ioPos;
			TRACE("CH %d  Read lead data size %d.\n",m_fileHead.channel,ioSize);
		}
		else
		{
			ioSize = bufSize;
		}

		ret = recFile.ReadPos(ioPos,frameBuf,ioSize);
		if(ret < 0 )
		{
			TRACE("CH%d read pos %d size %d  .\n",m_fileHead.channel,ioPos,bufSize);
			break;
		}

		//解析AVI数据包
		ret = AnalysisAVIData(ioPos,frameBuf,ioSize,eff_frmSize,vecIdx,detect_cnt);
		if(ret < 0)
		{
			endOffset = ioPos + eff_frmSize;
			break;
		}
		ioPos   += eff_frmSize;
		endOffset = ioPos;
	}

CHK_END:
	//真正的修复阶段
	//重新回写索引信息，暂时不修复AVI的索引
	//重写AVI头
	//AVI总长度更新
	RIFF_HEAD * pAviHead = (RIFF_HEAD * ) m_headBuf;
	pAviHead->size =  endOffset - 8;

	//MOV Len
	RIFF_HEAD * pMovHead = (RIFF_HEAD * ) (m_headBuf + STFS_REC_DATA_OFFSET - 8);
	pMovHead->size =  endOffset - 8 - STFS_REC_DATA_OFFSET;

	Main_AVIHeader * pAviMainHead =  (Main_AVIHeader *) (m_headBuf + 24);

	pAviMainHead->dwTotalFrame   = m_video_frames;

	AVISTREAMHEADER * pAviStreamH = (AVISTREAMHEADER *) (m_headBuf + 24 + sizeof(Main_AVIHeader) + 12);

	pAviStreamH->dwInitialFrames = 15;
	pAviStreamH->dwLength        = m_video_frames;

	//重写文件头
	m_fileHead.idxNum      = m_frmIdx.GetFrmIdxNum() + vecIdx.size();
	Repair_head_info();

	//重写索引
	if( 0!= vecIdx.size())
	{
		ioPos  = STFS_REC_IDX_OFFSET + m_frmIdx.GetDataPos();
		ioSize = vecIdx.size()* sizeof(STFS_REC_IDX);
		ret    = recFile.WritePos(ioPos,(uchar_t *)&vecIdx[0],ioSize);
		if(ret < 0)
		{
			TRACE("Recreate idx fail.ioPos %d ioSize %d.\n",ioSize,ioSize);
		}
	}
	endTime  = m_endTime;
	fileSize = endOffset;
	PrintTimeMs(m_fileHead.startTime,endTime,"Repair file time");
	TRACE_ERR("file size %d .\n",fileSize);

	recFile.Close();
	if(NULL != frameBuf)
	{
		delete [] frameBuf;
	}
	return 0;
}

int CAviFileWrite::AVI_write_frame(char *data, long bytes, int keyframe)
{
	int ret = 0;
	if(data == NULL)
	{
		return ERR_EMPTY_POINT;
	}

	ret = avi_write_data(data,bytes,0,keyframe);
	if(ret < 0)
	{
		return ret;
	}

	last_len = bytes;
	m_video_frames++;

	return ret;
}

int CAviFileWrite::AVI_write_audio(char *data, long bytes)
{
//	AVI->track[AVI->aptr].audio_bytes += bytes;
	int ret = avi_write_data(data,bytes,1,0);
	if( ret < 0 )
		return ret;

	track[m_aptr].audio_bytes += bytes;

	return 0;
}


int CAviFileWrite::avi_add_chunk(uchar_t *tag, unsigned char *data, int length)
{
   unsigned char c[8];
   int data_sz = 0;
   int ret     = 0;

   /* Copy tag and length int c, so that we need only 1 write system call
      for these two values */
   memcpy(c,tag,4);
   long2str(c+4,length);

   /* Output tag, length and data, restore previous position
      if the write fails */

   length = PAD_EVEN(length);

   memcpy(data,c,8);
   //些长度仅仅是数据的长度，不包含头包装,所写入数据需要再加上8
   ret = WriteFIFO(data,length + 8);

   return ret;
}

int CAviFileWrite::avi_add_index_entry( unsigned char *tag, long flags, unsigned long pos, unsigned long len)
{
	AVIOLD_INDEX * pIDX;

// 	if(m_idx_num>=m_max_idx)
// 	{
// 		ptr = realloc((void *)idx,(m_max_idx+4096)*16);
//
// 		if(ptr == 0)
// 		{
// 		//	AVI_errno = AVI_ERR_NO_MEM;
// 			return -1;
// 		}
// 		m_max_idx += 4096;
// 		idx = (unsigned char((*)[16]) ) ptr;
// 	}
// 	memcpy(idx[m_idx_no],tag,4);
// 	long2str(idx[m_idx_no]+ 4,flags);
// 	long2str(idx[m_idx_no]+ 8, pos);
// 	long2str(idx[m_idx_no]+12, len);

	if(m_idx_no >= m_max_idx)
	{
		m_aviIdx_bufsz = (m_max_idx + 4096) * 16 + 8;

		if(NULL != m_aviIdx)
		{
			delete [] m_aviIdx;
		}

		m_aviIdx = new uchar_t[m_aviIdx_bufsz];
		m_max_idx += 4096;
	}

	pIDX = (AVIOLD_INDEX*) m_aviIdx;

	if( 0 == m_idx_no)
	{
		pIDX->fcc = mmioFOURCC('i','d','x','l');
	}
	pIDX->date_size = (m_idx_no + 1)* 16;
	pIDX->aviIdx[m_idx_no].chunkId =  mmioFOURCC(tag[0],tag[1],tag[2],tag[3]);
	pIDX->aviIdx[m_idx_no].flags   = flags;
	pIDX->aviIdx[m_idx_no].offset  = pos;
	pIDX->aviIdx[m_idx_no].size    = len;
	/* Add index entry */

	//fprintf(stderr, "INDEX %s %ld %lu %lu\n", tag, flags, pos, len);

	/* Update counter */

	m_idx_no++;

	if(len > max_len)
		max_len = len;

	return 0;
}


int CAviFileWrite::avi_write_data(char *data, unsigned long length, int audio, int keyframe)
{
	int ret = 0;

	unsigned char astr[8] = {0};

	/* Check for maximum file length */

	/* Add index entry */

	//set tag for current audio track
	sprintf((char *)astr, "0%1dwb", (int)(m_aptr+1));

	if(keyframe)
	{
		//过滤掉音频不建立索引，因为存储的数据已经是混合音视频存储的。
// 		if(audio)
// 			ret = avi_add_index_entry(astr,0x00,m_curPos,length);
// 		else
		ret = avi_add_index_entry((unsigned char *)"00db",((keyframe)?0x10:0x0),m_curPos,length);
	}

	/* Output tag and data */

	if(audio)
		ret = avi_add_chunk(astr,(unsigned char *)data,length);
	else
		ret = avi_add_chunk((unsigned char *)"00db",(unsigned char *)data,length);

	return ret;
}

void CAviFileWrite::AVI_set_video(int width, int height, double fps, char *compressor)
{
	m_width  = width;
	m_height = height;
	m_fps    = fps;

	if(strncmp(compressor, "RGB", 3)==0)
	{
		memset(m_compressor, 0, 4);
	}
	else
	{
		memcpy(m_compressor,compressor,4);
	}
	m_compressor[4] = 0;
}

void CAviFileWrite::AVI_set_audio(int channels, long rate, int bits, int format, long mp3rate)
{
	/* may only be called if file is open for writing */
	//inc audio tracks
	m_aptr = m_anum;
	++m_anum;

	if(m_anum > AVI_MAX_TRACKS)
	{
		fprintf(stderr, "error - only %d audio tracks supported\n", AVI_MAX_TRACKS);

		return ;
	}

	track[m_aptr].a_chans = channels;
	track[m_aptr].a_rate  = rate;
	track[m_aptr].a_bits  = bits;
	track[m_aptr].a_fmt   = format;

	track[m_aptr].mp3rate = 0;

//	avi_update_header(AVI);
}



int CAviFileWrite::avi_sampsize(int j)
{
	int s;

	s = ((track[j].a_bits+7)/8)*track[j].a_chans;
	//   if(s==0) s=1; /* avoid possible zero divisions */
	if(s<4) s=4; /* avoid possible zero divisions */

	return s;
}

int CAviFileWrite::avi_update_movi_info(uint32_t movi_len)
{
	int ret = 0;

	uint32_t movi_pos = m_fileHead.dataOffset - 12;
	RIFF_HEAD movi_head;

	movi_head.fccType = mmioFOURCC('L','I','S','T');;// "LIST" ;
	movi_head.size    = movi_len;
	movi_head.ckID    = mmioFOURCC('m','o','v','i') ;//"movi"
	ret = m_pFile->WritePos(movi_pos,(uchar_t*)&movi_head,12);
	if(ret != 12)
	{
		return ret;
	}

	return ret;
}
//ThOe write preliminary AVI file header: 0 frames, max vid/aud size
int CAviFileWrite::avi_update_header()
{
   int njunk;
   int sampsize;
   int	hasIndex;
   int ms_per_frame;
   int frate;
   int flag;
   int movi_len;
   int hdrl_start;
   int strl_start;
   int j;
   long nhb = 0;

   //assume max size
// movi_len = AVI_MAX_LEN - HEADERBYTES + 4;
   movi_len = m_fileHead.fileSpace - m_fileHead.dataOffset + 4;
   //assume index will be written
   hasIndex=1;

   if(m_fps < 0.001)
   {
		frate = 0;
		ms_per_frame=0;
   }
   else
   {
		frate = (int) (FRAME_RATE_SCALE*m_fps + 0.5);
		ms_per_frame=(int) (1000000/m_fps + 0.5);
   }

   /* Prepare the file header */

   nhb = 0;

   /* The RIFF header */

   OUT4CC ("RIFF");
   OUTLONG(movi_len);    // assume max size
   OUT4CC ("AVI ");

   /* Start the header list */

   OUT4CC ("LIST");
   OUTLONG(0);        /* Length of list in bytes, don't know yet */
   hdrl_start = nhb;  /* Store start position */
   OUT4CC ("hdrl");

   /* The main AVI header */

   /* The Flags in AVI File header */

#define AVIF_HASINDEX           0x00000010      /* Index at end of file */
#define AVIF_MUSTUSEINDEX       0x00000020
#define AVIF_ISINTERLEAVED      0x00000100
#define AVIF_TRUSTCKTYPE        0x00000800      /* Use CKType to find key frames */
#define AVIF_WASCAPTUREFILE     0x00010000
#define AVIF_COPYRIGHTED        0x00020000

   OUT4CC ("avih");
   OUTLONG(56);                 /* # of bytes to follow */
   OUTLONG(ms_per_frame);       /* Microseconds per frame */
   //ThOe ->0
   //   OUTLONG(10000000);           /* MaxBytesPerSec, I hope this will never be used */
   OUTLONG(0);
   OUTLONG(0);                  /* PaddingGranularity (whatever that might be) */
                                /* Other sources call it 'reserved' */
   flag = AVIF_ISINTERLEAVED;
   if(hasIndex) flag |= AVIF_HASINDEX;
   if(hasIndex && m_must_use_index) flag |= AVIF_MUSTUSEINDEX;
   OUTLONG(flag);               /* Flags */
   OUTLONG(0);                  // no frames yet
   OUTLONG(0);                  /* InitialFrames */

   OUTLONG(m_anum+1);

   OUTLONG(0);                  /* SuggestedBufferSize */
   OUTLONG(m_width);         /* Width */
   OUTLONG(m_height);        /* Height */
                                /* MS calls the following 'reserved': */
   OUTLONG(0);                  /* TimeScale:  Unit used to measure time */
   OUTLONG(0);                  /* DataRate:   Data rate of playback     */
   OUTLONG(0);                  /* StartTime:  Starting time of AVI data */
   OUTLONG(0);                  /* DataLength: Size of AVI data chunk    */

   /* Start the video stream list ---------------------------------- */

   OUT4CC ("LIST");
   OUTLONG(0);        /* Length of list in bytes, don't know yet */
   strl_start = nhb;  /* Store start position */
   OUT4CC ("strl");

   /* The video stream header */

   OUT4CC ("strh");
   OUTLONG(56);                 /* # of bytes to follow */
   OUT4CC ("vids");             /* Type */
   OUT4CC (m_compressor);    /* Handler */

   OUTLONG(0);                  /* Flags */
   OUTLONG(0);                  /* Reserved, MS says: wPriority, wLanguage */
   OUTLONG(0);                  /* InitialFrames */
   OUTLONG(FRAME_RATE_SCALE);              /* Scale */
   OUTLONG(frate);              /* Rate: Rate/Scale == samples/second */
   OUTLONG(0);                  /* Start */
   OUTLONG(0);                  // no frames yet
   OUTLONG(0);                  /* SuggestedBufferSize */
   OUTLONG(-1);                 /* Quality */
   OUTLONG(0);                  /* SampleSize */
   OUTLONG(0);                  /* Frame */
   OUTLONG(0);                  /* Frame */
   //   OUTLONG(0);                  /* Frame */
   //OUTLONG(0);                  /* Frame */

   /* The video stream format */

   OUT4CC ("strf");
   OUTLONG(40);                 /* # of bytes to follow */
   OUTLONG(40);                 /* Size */
   OUTLONG(m_width);			 /* Width */
   OUTLONG(m_height);			/* Height */
   OUTSHRT(1); OUTSHRT(24);     /* Planes, Count */
   OUT4CC (m_compressor);    /* Compression */
   // ThOe (*3)
   OUTLONG(m_width*m_height*3);  /* SizeImage (in bytes?) */
   OUTLONG(0);                  /* XPelsPerMeter */
   OUTLONG(0);                  /* YPelsPerMeter */
   OUTLONG(0);                  /* ClrUsed: Number of colors used */
   OUTLONG(0);                  /* ClrImportant: Number of colors important */

   /* Finish stream list, i.e. put number of bytes in the list to proper pos */

   long2str(m_AVI_header+strl_start-4,nhb-strl_start);

   /* Start the audio stream list ---------------------------------- */

   for(j=0; j< m_anum; ++j)
   {
       sampsize = avi_sampsize(j);

       OUT4CC ("LIST");
       OUTLONG(0);        /* Length of list in bytes, don't know yet */
       strl_start = nhb;  /* Store start position */
       OUT4CC ("strl");

       /* The audio stream header */

       OUT4CC ("strh");
       OUTLONG(56);            /* # of bytes to follow */
       OUT4CC ("auds");

       // -----------
       // ThOe
       OUTLONG(0);             /* Format (Optionally) */
       // -----------

       OUTLONG(0);             /* Flags */
       OUTLONG(0);             /* Reserved, MS says: wPriority, wLanguage */
       OUTLONG(0);             /* InitialFrames */

       // ThOe /4
       OUTLONG(sampsize/4);      /* Scale */
       OUTLONG(1000*track[j].mp3rate/8);
       OUTLONG(0);             /* Start */
       OUTLONG(4*track[j].audio_bytes/sampsize);   /* Length */
       OUTLONG(0);             /* SuggestedBufferSize */
       OUTLONG(-1);            /* Quality */

       // ThOe /4
       OUTLONG(sampsize/4);    /* SampleSize */

       OUTLONG(0);             /* Frame */
       OUTLONG(0);             /* Frame */
       //       OUTLONG(0);             /* Frame */
       //OUTLONG(0);             /* Frame */

       /* The audio stream format */

       OUT4CC ("strf");
       OUTLONG(16);                   /* # of bytes to follow */
       OUTSHRT(track[j].a_fmt);           /* Format */
       OUTSHRT(track[j].a_chans);         /* Number of channels */
       OUTLONG(track[j].a_rate);          /* SamplesPerSec */
       // ThOe
       OUTLONG(1000*track[j].mp3rate/8);
       //ThOe (/4)

       OUTSHRT(sampsize/4);           /* BlockAlign */


       OUTSHRT(track[j].a_bits);          /* BitsPerSample */

       /* Finish stream list, i.e. put number of bytes in the list to proper pos */

       long2str(m_AVI_header+strl_start-4,nhb-strl_start);
   }

   /* Finish header list */

   long2str(m_AVI_header+hdrl_start-4,nhb-hdrl_start);


   /* Calculate the needed amount of junk bytes, output junk */

   njunk = HEADERBYTES - nhb - 8 - 12 + (m_fileHead.dataOffset - HEADERBYTES);

   /* Safety first: if njunk <= 0, somebody has played with
      HEADERBYTES without knowing what (s)he did.
      This is a fatal error */

   if(njunk <= 0)
   {
       fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
		return ERR_PARAM;
   }

   //在此处扩展更多的信息
   OUT4CC ("JUNK");
   OUTLONG(njunk);

   //2001-11-14 added id string

   if(njunk > strlen(id_str)+8)
   {
     //sprintf(id_str, "%s-%s", PACKAGE, VERSION);
     //memcpy(AVI_header+nhb, id_str, strlen(id_str));
   }

   nhb += njunk;


   /* Output the header, truncate the file to the number of bytes
      actually written, report an error if someting goes wrong */
   int ret = m_pFile->WritePos(0,(uchar_t *)m_AVI_header,HEADERBYTES);

   if(ret < 0)
   {
	   return ret;
   }

   /* Start the movi list */
#if 0
   OUT4CC ("LIST");
   OUTLONG(movi_len); /* Length of list in bytes */
   OUT4CC ("movi");
#endif
   ret = avi_update_movi_info(movi_len);
   if(ret < 0)
   {
	   TRACE_ERR("update movi info fail.\n");
   }

 //  TRACE("CH%02d Write head Ok  movi_len %d KB.\n",m_channel,movi_len >>10);

//    if ( lseek(fdes,0,SEEK_SET)<0 ||
//         avi_write(fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
// 	lseek(AVI->fdes,pos,SEEK_SET)<0)
//    {
//        AVI_errno = AVI_ERR_CLOSE;
//        return -1;
//    }

   return ret;
}

/*
  Write the header of an AVI file and close it.
  returns 0 on success, -1 on write error.
*/

int CAviFileWrite::avi_close_output_file()
{

   int ret, njunk, sampsize, hasIndex, ms_per_frame, m_frate, idxerror, flag;
   unsigned long movi_len;
   int hdrl_start, strl_start, j;
   long nhb;

#ifdef INFO_LIST
   long info_len;
//   time_t calptr;
#endif

   /* Calculate length of movi list */

   movi_len = m_curPos - HEADERBYTES + 4;

   /* Try to ouput the index entries. This may fail e.g. if no space
      is left on device. We will report this as an error, but we still
      try to write the header correctly (so that the file still may be
      readable in the most cases */

   idxerror = 0;
   //   fprintf(stderr, "pos=%lu, index_len=%ld             \n", AVI->pos, AVI->n_idx*16);
   //ret = avi_add_chunk((unsigned char *)"idx1", (unsigned char *)idx, m_idx_no*16);

   hasIndex = 0;
   //fprintf(stderr, "pos=%lu, index_len=%d\n", AVI->pos, hasIndex);

//    if(ret) {
//      idxerror = 1;
//      AVI_errno = AVI_ERR_WRITE_INDEX;
//    }

   /* Calculate Microseconds per frame */

   if(m_fps < 0.001)
   {
      m_frate=0;
      ms_per_frame=0;
   }
   else
   {
     m_frate = (int) (FRAME_RATE_SCALE * m_fps + 0.5);
     ms_per_frame=(int) (1000000/m_fps + 0.5);
   }

   /* Prepare the file header */

   nhb = 0;

   /* The RIFF header */
   uint32_t file_len = m_curPos + m_max_idx * 16 + 8;;

   OUT4CC ("RIFF");
   OUTLONG(file_len - 8);    /* # of bytes to follow */
   OUT4CC ("AVI ");

   /* Start the header list */

   OUT4CC ("LIST");
   OUTLONG(0);        /* Length of list in bytes, don't know yet */
   hdrl_start = nhb;  /* Store start position */
   OUT4CC ("hdrl");

   /* The main AVI header */

   /* The Flags in AVI File header */

#define AVIF_HASINDEX           0x00000010      /* Index at end of file */
#define AVIF_MUSTUSEINDEX       0x00000020
#define AVIF_ISINTERLEAVED      0x00000100
#define AVIF_TRUSTCKTYPE        0x00000800      /* Use CKType to find key frames */
#define AVIF_WASCAPTUREFILE     0x00010000
#define AVIF_COPYRIGHTED        0x00020000

   OUT4CC ("avih");
   OUTLONG(56);                 /* # of bytes to follow */
   OUTLONG(ms_per_frame);       /* Microseconds per frame */
   //ThOe ->0
   //   OUTLONG(10000000);           /* MaxBytesPerSec, I hope this will never be used */
   OUTLONG(0);
   OUTLONG(0);                  /* PaddingGranularity (whatever that might be) */
                                /* Other sources call it 'reserved' */
   flag = AVIF_ISINTERLEAVED;
   if(hasIndex)
	   flag |= AVIF_HASINDEX;
   if(hasIndex && m_must_use_index)
	   flag |= AVIF_MUSTUSEINDEX;
   OUTLONG(flag);               /* Flags */
   OUTLONG(m_video_frames);  /* TotalFrames */
   OUTLONG(0);                  /* InitialFrames */

   OUTLONG(m_anum+1);
//   if (AVI->track[0].audio_bytes)
//      { OUTLONG(2); }           /* Streams */
//   else
//      { OUTLONG(1); }           /* Streams */

   OUTLONG(0);                  /* SuggestedBufferSize */
   OUTLONG(m_width);         /* Width */
   OUTLONG(m_height);        /* Height */
                                /* MS calls the following 'reserved': */
   OUTLONG(0);                  /* TimeScale:  Unit used to measure time */
   OUTLONG(0);                  /* DataRate:   Data rate of playback     */
   OUTLONG(0);                  /* StartTime:  Starting time of AVI data */
   OUTLONG(0);                  /* DataLength: Size of AVI data chunk    */


   /* Start the video stream list ---------------------------------- */

   OUT4CC ("LIST");
   OUTLONG(0);        /* Length of list in bytes, don't know yet */
   strl_start = nhb;  /* Store start position */
   OUT4CC ("strl");

   /* The video stream header */

   OUT4CC ("strh");
   OUTLONG(56);                 /* # of bytes to follow */
   OUT4CC ("vids");             /* Type */
   OUT4CC (m_compressor);		/* Handler */
   OUTLONG(0);                  /* Flags */
   OUTLONG(0);                  /* Reserved, MS says: wPriority, wLanguage */
   OUTLONG(0);                  /* InitialFrames */
   OUTLONG(FRAME_RATE_SCALE);              /* Scale */
   OUTLONG(m_frate);              /* Rate: Rate/Scale == samples/second */
   OUTLONG(0);                  /* Start */
   OUTLONG(m_video_frames);  /* Length */
   OUTLONG(0);                  /* SuggestedBufferSize */
   OUTLONG(-1);                 /* Quality */
   OUTLONG(0);                  /* SampleSize */
   OUTLONG(0);                  /* Frame */
   OUTLONG(0);                  /* Frame */
   //   OUTLONG(0);                  /* Frame */
   //OUTLONG(0);                  /* Frame */

   /* The video stream format */

   OUT4CC ("strf");
   OUTLONG(40);                 /* # of bytes to follow */
   OUTLONG(40);                 /* Size */
   OUTLONG(m_width);         /* Width */
   OUTLONG(m_height);        /* Height */
   OUTSHRT(1); OUTSHRT(24);     /* Planes, Count */
   OUT4CC (m_compressor);    /* Compression */
   // ThOe (*3)
   OUTLONG(m_width*m_height*3);  /* SizeImage (in bytes?) */
   OUTLONG(0);                  /* XPelsPerMeter */
   OUTLONG(0);                  /* YPelsPerMeter */
   OUTLONG(0);                  /* ClrUsed: Number of colors used */
   OUTLONG(0);                  /* ClrImportant: Number of colors important */

   /* Finish stream list, i.e. put number of bytes in the list to proper pos */

   long2str(m_AVI_header+strl_start-4,nhb-strl_start);

   /* Start the audio stream list ---------------------------------- */

   for(j=0; j< m_anum; ++j)
   {

     //if (AVI->track[j].a_chans && AVI->track[j].audio_bytes)
       {
	 sampsize = avi_sampsize(j);

	 OUT4CC ("LIST");
	 OUTLONG(0);        /* Length of list in bytes, don't know yet */
	 strl_start = nhb;  /* Store start position */
	 OUT4CC ("strl");

	 /* The audio stream header */

	 OUT4CC ("strh");
	 OUTLONG(56);            /* # of bytes to follow */
	 OUT4CC ("auds");

	 // -----------
	 // ThOe
	 OUTLONG(0);             /* Format (Optionally) */
	   // -----------

	 OUTLONG(0);             /* Flags */
	 OUTLONG(0);             /* Reserved, MS says: wPriority, wLanguage */
	 OUTLONG(0);             /* InitialFrames */

	 // ThOe /4
	 OUTLONG(sampsize/4);      /* Scale */
	 OUTLONG(1000*track[j].mp3rate/8);
	 OUTLONG(0);             /* Start */
	 OUTLONG(4*track[j].audio_bytes/sampsize);   /* Length */
	 OUTLONG(0);             /* SuggestedBufferSize */
	 OUTLONG(-1);            /* Quality */

	 // ThOe /4
	 OUTLONG(sampsize/4);    /* SampleSize */

	 OUTLONG(0);             /* Frame */
	 OUTLONG(0);             /* Frame */
	 //	 OUTLONG(0);             /* Frame */
	 //OUTLONG(0);             /* Frame */

	 /* The audio stream format */

	 OUT4CC ("strf");
	 OUTLONG(16);                   /* # of bytes to follow */
	 OUTSHRT(track[j].a_fmt);           /* Format */
	 OUTSHRT(track[j].a_chans);         /* Number of channels */
	 OUTLONG(track[j].a_rate);          /* SamplesPerSec */
	 // ThOe
	 OUTLONG(1000*track[j].mp3rate/8);
	 //ThOe (/4)

	 OUTSHRT(sampsize/4);           /* BlockAlign */


	 OUTSHRT(track[j].a_bits);          /* BitsPerSample */

	 /* Finish stream list, i.e. put number of bytes in the list to proper pos */
       }
       long2str(m_AVI_header+strl_start-4,nhb-strl_start);
   }

   /* Finish header list */

   long2str(m_AVI_header+hdrl_start-4,nhb-hdrl_start);
   // add INFO list --- (0.6.0pre4)

#ifdef INFO_LIST
   OUT4CC ("LIST");

   //FIXME
   info_len = MAX_INFO_STRLEN + 12;
   OUTLONG(info_len);
   OUT4CC ("INFO");

//   OUT4CC ("INAM");
//   OUTLONG(MAX_INFO_STRLEN);

//   sprintf(id_str, "\t");
//   memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
//   memcpy(AVI_header+nhb, id_str, strlen(id_str));
//   nhb += MAX_INFO_STRLEN;

   OUT4CC ("ISFT");
   OUTLONG(MAX_INFO_STRLEN);

   //sprintf(id_str, "%s-%s", PACKAGE, VERSION);
   memset(m_AVI_header+nhb, 0, MAX_INFO_STRLEN);
   //memcpy(AVI_header+nhb, id_str, strlen(id_str));
   nhb += MAX_INFO_STRLEN;

//   OUT4CC ("ICMT");
//   OUTLONG(MAX_INFO_STRLEN);

//   calptr=time(NULL);
//   sprintf(id_str, "\t%s %s", ctime(&calptr), "");
//   memset(AVI_header+nhb, 0, MAX_INFO_STRLEN);
//   memcpy(AVI_header+nhb, id_str, 25);
//   nhb += MAX_INFO_STRLEN;
#endif

   // ----------------------------

   /* Calculate the needed amount of junk bytes, output junk */
   njunk = HEADERBYTES - nhb - 8 - 12 + (m_fileHead.dataOffset - HEADERBYTES);


   /* Safety first: if njunk <= 0, somebody has played with
      HEADERBYTES without knowing what (s)he did.
      This is a fatal error */

   if(njunk<=0)
   {
      fprintf(stderr,"AVI_close_output_file: # of header bytes too small\n");
      return ERR_PARAM;
   }

   OUT4CC ("JUNK");
   OUTLONG(njunk);

   //memset(m_AVI_header+nhb,0,njunk);


   /* Start the movi list */

   movi_len = m_curPos - m_fileHead.dataOffset;

   /* Output the header, truncate the file to the number of bytes
      actually written, report an error if someting goes wrong */

//    if ( lseek(AVI->fdes,0,SEEK_SET)<0 ||
//         avi_write(AVI->fdes,(char *)AVI_header,HEADERBYTES)!=HEADERBYTES ||
//         ftruncate(AVI->fdes,AVI->pos)<0 )
//    {
//       AVI_errno = AVI_ERR_CLOSE;
//       return -1;
//    }

	ret = m_pFile->WritePos(0,(uchar_t*)m_AVI_header,HEADERBYTES);
	if(ret != HEADERBYTES)
	{
		return ret;
	}
	//结束写入movi info
	ret = avi_update_movi_info(movi_len);
	if(ret < 0)
	{
		TRACE_ERR("update movi info fail.\n");
	}

   return ret;
}

/*******************************************************************
 *                                                                 *
 *    Utilities for reading video and audio from an AVI File       *
 *                                                                 *
 *******************************************************************/

int CAviFileWrite::AVI_close()
{
   int ret;

   /* If the file was open for writing, the header and index still have
      to be written */


   ret = avi_close_output_file();
   if ( NULL != bitmap_info_header)
   {
	   free(bitmap_info_header);
   }


   return ret;
}
